package com.sc.sc250601.cook.util;

import com.fasterxml.jackson.core.type.TypeReference; // 这是Jackson的类，正确！
import com.fasterxml.jackson.databind.ObjectMapper;
import com.sc.sc250601.cook.pojo.Post;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.RedisScriptingCommands;
import org.springframework.data.redis.connection.ReturnType;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

// 1. 标记为Spring组件，确保能被扫描并注入依赖
@Component
public class StringRedisTemplateUtil {

    // 2. 注入Spring默认的StringRedisTemplate（操作Redis字符串类型）
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    // 3. 注入Spring自动配置的ObjectMapper（推荐，全局统一配置）
    @Autowired
    private ObjectMapper objectMapper;

    // ------------------- 存储方法 -------------------
    // 存储对象（序列化为JSON），带过期时间（单位：秒）
    public void setObject(String key, Object value, long expireSeconds) {
        try {
            // 将对象序列化为JSON字符串
            String jsonValue = objectMapper.writeValueAsString(value);
            stringRedisTemplate.opsForValue().set(key, jsonValue, expireSeconds, TimeUnit.SECONDS);
        } catch (Exception e) {
            throw new RedisOperationException("JSON序列化失败", e);
        }
    }

    // ------------------- 读取方法 -------------------
    // 读取普通对象（无泛型，如Post、User等）
    public <T> T getObject(String key, Class<T> clazz) {
        try {
            String json = stringRedisTemplate.opsForValue().get(key);
            if (json == null) {
                return null;
            }
            // 反序列化为指定类型
            return objectMapper.readValue(json, clazz);
        } catch (Exception e) {
            throw new RedisOperationException("JSON反序列化失败", e);
        }
    }

    public <T> T getObject(String key, TypeReference<T> typeReference) {
        try {
            String json = stringRedisTemplate.opsForValue().get(key);
            System.out.println("从Redis读取的原始JSON：" + json); // 打印原始JSON
            if (json == null) {
                System.out.println("Redis中无此键：" + key);
                return null;
            }
            T result = objectMapper.readValue(json, typeReference);
            System.out.println("Redis反序列化成功：" + result);
            return result;
        } catch (Exception e) {
            System.out.println("Redis反序列化失败！" + e.getMessage()); // 打印异常
            throw new RedisOperationException("JSON反序列化失败", e);
        }
    }
    /**
     * 根据模式匹配获取所有键（慎用！大数据量下可能阻塞Redis）
     * @param pattern 匹配模式（如 "post:view:count:*"）
     * @return 匹配的键集合
     */
    public Set<String> keys(String pattern) {
        return stringRedisTemplate.keys(pattern);
    }


    //删除
    /**
     * 删除单个缓存键
     * @param key 缓存键（如 "AllPosts"）
     * @return 是否删除成功
     */
    public Boolean delete(String key) {
        return stringRedisTemplate.delete(key);
    }

    /**
     * 批量删除多个缓存键
     * @param keys 缓存键集合（如 ["AllPosts", "uids:3"]）
     * @return 删除的键数量
     */
    public Long delete(Collection<String> keys) {
        return stringRedisTemplate.delete(keys);
    }

    /**
     * 按前缀删除缓存（如删除所有 "uids:" 开头的键）
     * @param prefix 前缀（如 "uids:"）
     * @return 删除的键数量
     */
    public Long deleteByPrefix(String prefix) {
        // 匹配所有以 prefix 开头的键
        Set<String> keys = stringRedisTemplate.keys(prefix + "*");
        if (keys == null || keys.isEmpty()) {
            return 0L;
        }
        return stringRedisTemplate.delete(keys);
    }
    // 工具方法：带随机延迟的缓存删除（避免雪崩）
    private void deleteCacheWithDelay(String key) {
        // 随机延迟 0-100ms，分散缓存重建时间
        long delay = new Random().nextInt(100);
        CompletableFuture.runAsync(() -> {
            try {
                Thread.sleep(delay);
                stringRedisTemplate.delete(key);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        });
    }

    // ------------------- 自定义异常 -------------------
    public static class RedisOperationException extends RuntimeException {
        public RedisOperationException(String message, Throwable cause) {
            super(message, cause);
        }
    }

    /**
     * 点赞/取消点赞
     * @param postId 帖子ID
     * @param userId 点赞用户ID
     * @return 操作结果：1=点赞成功，0=取消点赞成功，-1=重复点赞
     */
    // 在 StringRedisTemplateUtil.java 中修改 likeOrCancel 方法
    public Integer likeOrCancel(Long postId, Long userId) {
        // 1. 非空校验（必须）
        if (postId == null || userId == null) {
            throw new IllegalArgumentException("postId 和 userId 不能为空");
        }

        // 2. 键名定义
        String postLikeSetKey = "post:like:set:" + postId;
        String postLikeCountKey = "post:like:count:" + postId;
        String userLikeSetKey = "user:like:set:" + userId;

        // 3. Lua 脚本（不变，但确保返回 0/1 是数字）
        String luaScript = "local isLiked = redis.call('SISMEMBER', KEYS[1], ARGV[1]) " +
                "if isLiked == 1 then " +
                "   redis.call('SREM', KEYS[1], ARGV[1]) " +
                "   redis.call('SREM', KEYS[3], ARGV[2]) " +
                "   redis.call('DECR', KEYS[2]) " +
                "   return 0 " + // 明确返回数字 0
                "else " +
                "   redis.call('SADD', KEYS[1], ARGV[1]) " +
                "   redis.call('SADD', KEYS[3], ARGV[2]) " +
                "   redis.call('INCR', KEYS[2]) " +
                "   return 1 " + // 明确返回数字 1
                "end";

        // 4. 执行脚本：用 StringRedisTemplate 直接执行，返回 Long
        // 关键：不依赖 DefaultRedisScript 的泛型，直接指定返回类型为 Long
        Long result = (Long) stringRedisTemplate.execute(
                (RedisCallback<Long>) connection -> {
                    RedisScriptingCommands commands = connection.scriptingCommands();
                    // 执行脚本，参数：脚本内容、键数量、键数组、参数数组
                    return commands.eval(
                            luaScript.getBytes(),
                            ReturnType.INTEGER, // 显式指定返回类型为 INTEGER（对应 long）
                            3, // 键的数量（KEYS[1], KEYS[2], KEYS[3] 共3个）
                            postLikeSetKey.getBytes(),
                            postLikeCountKey.getBytes(),
                            userLikeSetKey.getBytes(),
                            userId.toString().getBytes(), // ARGV[1]
                            postId.toString().getBytes()  // ARGV[2]
                    );
                }
        );

        // 5. 结果处理
        if (result == null) {
            throw new RedisOperationException("点赞脚本执行失败", null);
        }
        return result.intValue(); // 0/1 转 Integer 安全
    }
    //获取帖子点赞数
    public Long getPostLikeCount(Long postId) {
        String key = "post:like:count:" + postId;
        String countStr = stringRedisTemplate.opsForValue().get(key);
        return countStr == null ? 0L : Long.parseLong(countStr);
    }
    //判断用户是否已点赞
    public Boolean isUserLikedPost(Long postId, Long userId) {
        String key = "post:like:set:" + postId;
        // 正确用法：通过opsForSet()获取Set操作对象，再调用isMember
        return stringRedisTemplate.opsForSet().isMember(key, userId.toString());
    }
    //获取用户的点赞列表
    public Set<Long> getUserLikedPosts(Long userId) {
        String key = "user:like:set:" + userId;
        // SMEMBERS：获取集合中所有元素（帖子ID）
        Set<String> postIdStrs = stringRedisTemplate.opsForSet().members(key);
        if (postIdStrs == null || postIdStrs.isEmpty()) {
            return Collections.emptySet();
        }
        // 转换为Long类型
        return postIdStrs.stream().map(Long::parseLong).collect(Collectors.toSet());
    }
}